import os
import zmq
from datetime import datetime

import pdb
import levels
from publisher import NoOpPublisher


# class DUT(object):
#     def __init__(self, index):
#         self.index = index
#         self.sn = ''
#         self.running_item = None


class ReportLogger(object):

    index = 0

    @staticmethod
    def next_index():
        ReportLogger.index += 1
        return ReportLogger.index


class FileLogger(ReportLogger):

    def __init__(self, log_folder='/tmp/',publisher=NoOpPublisher(), suffix='file_logger.txt', level=levels.DEBUG):
        super(FileLogger, self).__init__()
        self.log_path_ = os.path.join(log_folder,suffix)
        self.publisher = publisher
        # self.site_count = site_count
        # self.sub_per_site = sub_per_site
        # self.file_handles = [[None for sub in range(sub_per_site)] for site in range(site_count)]
        # self.log_paths = [[None for sub in range(sub_per_site)] for site in range(site_count)]
        self.file_handle_ = open(self.log_path_,"w+")
        self.context_buffer_ = ""
        # self.devices = {i: None for i in range(site_count)}
        # self.uut_sn = ['' for i in range(site_count)]
        # self.suffix_ = suffix
        self.level_ = level
        self.need_timestamp = []
        self.auto_line_ending = []

    # def get_log_path(self, site):
    #     if not self.log_paths:
    #         return ''
    #     elif len(self.log_paths) > site:
    #         return self.log_paths[site] if self.log_paths[site] else ''
    #     else:
    #         return self.log_paths[0] if self.log_paths[0] else ''

    def _attach_log(self, handle):
        if handle:
            self.file_handle_ = handle

    def release_log(self):
        file = self.file_handle_
        #pdb.set_trace()
        if file and not file.closed:
            if len(self.context_buffer_)>0:
                self.write(self.context_buffer_)
            file.close()
            self.file_handle_ = None

    def __del__(self):
        self.release_log()

    @staticmethod
    def __timestamp(data):
        ts = str(datetime.now())
        return ts + ' '*10 + data

    @staticmethod
    def default_log_formatter(msg, threshold):
        if len(msg) == 5:
            topic, ts, level, origin, data = msg[:]
            if int(level) > threshold:
                return None
            return data
        else:
            return str(msg)

    def default_log(self, socket, msg):
        # id = int(socket.getsockopt(zmq.IDENTITY)[-1])     # IDENTITY is port number aligned to 10s
        # site = id / self.sub_per_site
        # sub = id % self.sub_per_site
        data = self.default_log_formatter(msg, self.level_)
        if not data:
            return
        ts = str(datetime.now())
        if socket in self.auto_line_ending:
            data = str(data) + os.linesep
            if socket in self.need_timestamp:
                data = self.__timestamp(data)
            self.write(data)
        else:
            # pdb.set_trace()
            s = self.context_buffer_ + str(data)  # concat with last time leftover string
            self.context_buffer_ = ''
            s = s.replace('\r', '\n')       # unify the line ending char
            s = s.replace('\n\n', '\n')     # eliminate double line ending
            lines = s.split('\n')           # split per screen lines, last slice have no original line ending
            for i in range(len(lines)):
                data = lines[i]
                if i < len(lines) - 1:
                    # The last n-1 line has '\n', so we write it to log.
                    if socket in self.need_timestamp:
                        data = self.__timestamp(data)
                    self.write(data + os.linesep)
                else:
                    # This is the last line, it hasn't '\n', so we will not write it.
                    self.context_buffer_ = data # Why, werid behavior

    def write(self, data):
        log = self.file_handle_
        if log is None:
            print("Failed to write log to file. abandon > {0}".format(data))
            return
        log.write(str(data))
        log.flush()

    # def event_dispatch(self, socket, msg):
    #     sequencer_event_map = {
    #         events.SEQUENCE_START: 'on_sequence_start',
    #         events.SEQUENCE_END: 'on_sequence_end',
    #         events.ITEM_START: 'on_item_start',
    #         events.ITEM_FINISH: 'on_item_finish',
    #         events.ATTRIBUTE_FOUND: 'on_attribute_found',
    #         events.SEQUENCE_LOADED: 'on_sequence_loaded'
    #     }
    #     site = int(socket.getsockopt(zmq.IDENTITY)[-1])  # IDENTITY is port number aligned to 10s
    #     if len(msg) == 5:
    #         topic, ts, level, origin, data = msg[:]
    #         if int(level) > levels.REPORTER:
    #             return
    #         report = ReporterProtocol.parse_report(data)
    #         if report:
    #             func_name = sequencer_event_map.get(report.event)
    #             if func_name:
    #                 func = getattr(self, func_name, None)
    #                 if callable(func):
    #                     func(site, report.data)
    #             else:
    #                 print 'Unrecognized event; {}'.format(report.event), msg

    # def on_sequence_start(self, site, data):
    #     ts = datetime.strftime(datetime.now(), '%m-%d-%H-%M-%S')
    #     for sub in range(self.sub_per_site):
    #         if self.sub_alias:
    #             mid_name = self.sub_alias[sub]
    #         elif self.sub_per_site == 1:
    #             mid_name = ''
    #         else:
    #             mid_name = 'CH{}'.format(sub)
    #         filename = '_'.join([self.product, self.station_type, 'UUT{}'.format(site), mid_name, ts, self.suffix])
    #         file_path = os.path.join(self.log_folder, filename)
    #         self.log_paths[site][sub] = file_path
    #         log_f = open(file_path, 'w+')
    #         self.release_log(site, sub)
    #         self._attach_log(site, sub, log_f)
    #         self.context_buffer[site][sub] = ''
    #     self.devices[site] = DUT(ReportLogger.next_index())
    #     func = getattr(self, 'post_sequence_start', None)
    #     if callable(func):
    #         func(site, data)

    # def on_sequence_end(self, site, data):
    #     for sub in range(self.sub_per_site):
    #         if len(self.context_buffer[site][sub]) > 0:
    #             self.write(site, sub, self.context_buffer[site][sub] + os.linesep)
    #         self.release_log(site, sub)
    #     func = getattr(self, 'post_sequence_end', None)
    #     if callable(func):
    #         func(site, data)
